package entities;

import entities.exceptions.CrossingOccupiedException;
import java.awt.Point;
import java.util.LinkedList;

/**
 * Crossing Class. 
 * Manages crossing at each Tile.
 * @author mateusz
 */
public class Crossing {

    /**
     * List of incoming robots
     */
    protected volatile LinkedList<Robot> incomingRobots;
    /**
     * List of outcoming robots
     */
    protected volatile LinkedList<Robot> outcomingRobots;
    /**
     * If crossing is unlimited (e.g. in factory, supply)
     */
    protected boolean unlimited;

    /**
     * Creates new limited crossing
     */
    public Crossing() {
        unlimited = false;

        incomingRobots = new LinkedList<Robot>();
        outcomingRobots = new LinkedList<Robot>();
    }

    /**
     * Occupy crossing. Crossing can be occupied by Robot if all others Robots 
     * at this crossing has the same direction with him and all incoming Robots 
     * has different twist as him.
     * @param r                             Robot that wants to take crossing
     * @throws CrossingOccupiedException    If robot cant take crossing
     */
    public synchronized void occupy(Robot r) throws CrossingOccupiedException {
        if (!unlimited) {
            boolean canTake = true;
            // check for directions of all robots if matches yours
            for (Robot anotherRobot : incomingRobots) {
                if (!sameDirection(r, anotherRobot)) {
                    canTake = false;
                }

                // check for same twist with another icoming
                if (sameTwist(r, anotherRobot)) {
                    canTake = false;
                }
            }

            for (Robot anotherRobot : outcomingRobots) {
                if (!sameDirection(r, anotherRobot)) {
                    canTake = false;
                }
            }

            if (!canTake) {
                throw new CrossingOccupiedException();
            }
        }
        
        incomingRobots.add(r);
    }

    /**
     * Release this crossing
     * @param r Robot that releases crossing
     */
    public synchronized void unoccupy(Robot r) {
        // in case of robot kill while was incoming
        if (!outcomingRobots.remove(r)) {
            incomingRobots.remove(r);
        }
        //WilliSpace.notifyAllRobots();
    }

    /**
     * Tell crossing that Robot becomes outComing
     * @param r
     */
    public synchronized void goingOut(Robot r) {
        incomingRobots.remove(r);
        outcomingRobots.add(r);
    }

    /**
     * Check if any robot is at crossing
     * @return true if any robot is at crossing
     */
    public boolean isOccupied() {
        return incomingRobots.size() > 0 || outcomingRobots.size() > 0;
    }

    /**
     * Set this crossing unlimited
     */
    public void setUnlimited() {
        this.unlimited = true;
    }

    /**
     * Set this crossing limited
     */
    public void setLimited() {
        this.unlimited = false;
    }

    /**
     * Check if crossing is unlimited 
     * @return true if crossing is unlimited
     */
    public boolean isUnlimited() {
        return this.unlimited;
    }

    private static boolean sameDirection(Robot r1, Robot r2) {
        boolean sameDirection = false;
        Point r1Directions = r1.getDirections();
        Point r2Directions = r2.getDirections();

        int x1 = r1Directions.x, x2 = r2Directions.x, y1 = r1Directions.y, y2 = r2Directions.y;

        if (x1 == x2 && y1 == y2) {
            sameDirection = true;
        } else if (x1 == (-1) * x2 && y1 == (-1) * y2) {
            sameDirection = true;
        }

        return sameDirection;
    }

    private static boolean sameTwist(Robot r1, Robot r2) {
        Point r1Directions = r1.getDirections();
        Point r2Directions = r2.getDirections();

        return r1Directions.x == r2Directions.x && r1Directions.y == r2Directions.y;
    }
}
